﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace V32VM
{
    class VM
    {
        public readonly Registers regs;
        public readonly Memory memory;
        //bool interrupts_on;
        // start address of interrupt table
        //uint int_table;

        public bool done { get; private set; }

        public VM(Registers regs, Memory memory)
        {
            this.regs = regs;
            this.memory = memory;
            //this.interrupts_on = false;
            //this.int_table = 0;
            this.done = false;
        }

        public void step()
        {
            if (done)
                throw new InvalidOperationException("Cannot step when the machine has stopped.");

            /* NOTE: in the following, if a command does not have a certain argument,
             *   the bit/value relating to that argument is assumed to be 0
             * 
             * for byte 1:[6-7], "size" is only relevant for indirection.
             *   size = true -> use a whole word pointed to by the address
             *   size = false -> use only a single byte pointed to by the address
             * 
             * byte 1
             * ------
             * [0-5]: opcode
             * [6]: size of first operand (0=1 byte, 1=4 bytes)
             * [7]: size of second operand
             * 
             * byte 2
             * ------
             * [0-1]: arg1 type
             * [2-3]: arg2 type
             * [4]: arg1 relative
             * [5]: arg2 relative
             * [6]: arg1 offset size
             * [7]: arg2 offset size
             */

            byte instr0 = memory[regs.ip, regs.ip];
            if (instr0 == 21) iret(regs.ip);
            else
            {
                byte instr1 = memory[regs.ip, regs.ip + 1];

                byte opcode = (byte)(instr0 & ((1 << 6) - 1));
                bool sizeLeft = (instr0 & (1 << 6)) != 0;
                bool sizeRight = (instr0 & (1 << 7)) != 0;

                byte typeLeft = (byte)(instr1 & 3);
                byte typeRight = (byte)((instr1 >> 2) & 3);
                bool leftRelative = (instr1 & (1 << 4)) != 0;
                bool rightRelative = (instr1 & (1 << 5)) != 0;
                bool leftRelativeSize = (instr1 & (1 << 6)) != 0;
                bool rightRelativeSize = (instr1 & (1 << 7)) != 0;

                switch (opcode)
                {
                    case 0: ld(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 1: add(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 2: sub(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 3: mul(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 4: div(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 5: mod(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 6: and(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 7: or(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 8: xor(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 9: lshift(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 10: rshift(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 11: inv(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 12: neg(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 13: clr(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    // the following commands do not have dest-args (just a note)
                    case 14: cmp(regs.ip, sizeLeft, sizeRight, typeLeft, typeRight, leftRelative, rightRelative, leftRelativeSize, rightRelativeSize); break;
                    case 15: jmp(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 16: je(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 17: jne(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 18: jg(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 19: jl(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 20: jle(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 21: jge(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    case 22: _int(regs.ip, sizeLeft, typeLeft, leftRelative, leftRelativeSize); break;
                    default: throw new Exception("Invalid opcode '" + opcode + "'.");
                }
            }
        }

        public uint getReg(uint index)
        {
            switch (index)
            {
                case 0: return regs.a;
                case 1: return regs.b;
                case 2: return regs.c;
                case 3: return regs.d;
                case 4: return regs.e;
                case 5: return regs.f;
                case 6: return regs.g;
                case 7: return regs.t;
                case 8: return regs.u;
                case 9: return regs.v;
                case 10: return regs.w;
                case 11: return regs.x;
                case 12: return regs.y;
                case 13: return regs.z;
                case 14: return regs.s;
                case 15: return regs.ip;
                case 16: return regs.sp;
                case 17: return regs.fl;
                default: throw new Exception("No register with id " + index + ".");
            }
        }
        byte getByte(uint i)
        {
            if ((byte)i == i)
                return (byte)i;
            throw new Exception(i + " is too large for a byte.");
        }
        void setReg(uint index, uint value)
        {
            switch (index)
            {
                case 0: regs.a = getByte(value); break;
                case 1: regs.b = getByte(value); break;
                case 2: regs.c = getByte(value); break;
                case 3: regs.d = getByte(value); break;
                case 4: regs.e = getByte(value); break;
                case 5: regs.f = getByte(value); break;
                case 6: regs.g = getByte(value); break;
                case 7: regs.t = value; break;
                case 8: regs.u = value; break;
                case 9: regs.v = value; break;
                case 10: regs.w = value; break;
                case 11: regs.x = value; break;
                case 12: regs.y = value; break;
                case 13: regs.z = value; break;
                case 14: regs.s = value; break;
                case 15: regs.ip = value; break;
                case 16: regs.sp = value; break;
                case 17: regs.fl = getByte(value); break;
                default: throw new Exception("No register with id " + index + ".");
            }
        }

        public const byte
            FLAG_EQ = 0,
            FLAG_LESS = 1,
            FLAG_OVERFLOW = 2;
        public bool getFlag(byte flagIndex)
        {
            return (regs.fl & (1 << flagIndex)) != 0;
        }
        void setFlag(byte flagIndex, bool value)
        {
            if (value)
                regs.fl |= (byte)(1 << flagIndex);
            else regs.fl &= (byte)(~(1 << flagIndex));
        }

        /* for typeLeft,typeRight
         *   00: register
         *   01: constant
         *   02: [register]
         *   03: [constant]
         */
        const bool SIZE_WORD = true, 
            SIZE_BYTE = false;
        const byte TYPE_REG = 0,
            TYPE_CONST = 1,
            TYPE_REG_INDIRECT = 2,
            TYPE_CONST_INDIRECT = 3;

        uint argSize(bool size, byte type, bool relative, bool relativeSize)
        {
            // register = 1 byte, constant = 4 bytes
            uint bytes = (type % 2 == 0) ? 1u : 4u;
            if (relative)
                bytes += (uint)((relativeSize == SIZE_BYTE) ? 1 : 4);
            return bytes;
        }
        uint srcArgValue(uint ip, uint address, bool size, byte type, bool relative, bool relativeSize)
        {
            // address points to start of argument data
            // size = use word/byte from indirection
            // type = argument type
            // relativeSize = word/byte size of offset

            uint val = 0;

            if (type >= 4)
                throw new Exception("Invalid argument type '" + type + "'.");

            if (type % 2 == 0) // register/[register]
            {
                val = getReg(memory[ip, address]);
                address++;
            }
            else // constant/[constant]
            {
                // constants are always 4 bytes
                val = memory.words[ip, address];
                address += 4;
            }

            if (relative)
            {
                if (relativeSize == SIZE_WORD)
                {
                    val += memory.words[ip, address];
                    address += 4;
                }
                else
                {
                    val += memory[ip, address];
                    address++;
                }
            }

            if (type >= 2) // indirect ([register]/[constant])
            {
                if (size == SIZE_WORD)
                {
                    val = memory.words[ip, val];
                }
                else
                {
                    val = memory[ip, val];
                }
            }

            return val;
        }
        void setDestValue(uint ip, uint address, bool size, byte type, bool relative, bool relativeSize, uint srcValue)
        {
            // address points to start of argument data
            // size = use word/byte from indirection
            // type = argument type
            // relativeSize = word/byte size of offset

            if (type >= 4 || type == 1) // 1 = constant; cannot set a constant to a value...
                throw new Exception("Invalid argument type '" + type + "'.");

            if (type == 0) // register
            {
                setReg(memory[ip, address], srcValue);
            }
            else if (type == 2) // [register]
            {
                uint pointer = getReg(memory[ip, address]);
                address++;
                if (relative)
                {
                    if (relativeSize == SIZE_WORD)
                    {
                        pointer += memory.words[ip, address];
                        address += 4;
                    }
                    else
                    {
                        pointer += memory[ip, address];
                        address++;
                    }
                }
                if (size == SIZE_WORD)
                    memory.words[ip, pointer] = srcValue;
                else memory[ip, pointer] = (byte)srcValue;
            }
            else if (type == 3) // [constant]
            {
                uint pointer = memory.words[ip, address];
                address += 4;
                if (relative)
                {
                    if (relativeSize == SIZE_WORD)
                    {
                        pointer += memory.words[ip, address];
                        address += 4;
                    }
                    else
                    {
                        pointer += memory[ip, address];
                        address++;
                    }
                }
                if (size == SIZE_WORD)
                    memory.words[ip, pointer] = srcValue;
                else memory[ip, pointer] = (byte)srcValue;
            }
            else throw new Exception("We already talked about this...");
        }

        #region commands
        void ld(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void add(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) +
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void sub(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) -
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void mul(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) *
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void div(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            uint right = srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize);
            if (right == 0)
                throw new Exception("Divide by 0!");
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) / right);

            regs.ip = arg2Address + arg2Size;
        }
        void mod(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) %
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void and(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) &
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void or(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) |
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void xor(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) ^
                srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void lshift(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) <<
                (int)srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void rshift(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize,
                srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize) >>
                (int)srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize));

            regs.ip = arg2Address + arg2Size;
        }
        void inv(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint val = srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            val = ~val; // bitwise invert
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize, val);

            regs.ip = arg1Address + arg1Size;
        }
        void neg(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint val = srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            val = ~val + 1; // 2's complement negation
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize, val);

            regs.ip = arg1Address + arg1Size;
        }
        void clr(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            setDestValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize, 0);

            regs.ip = arg1Address + arg1Size;
        }
        void cmp(uint ip, bool sizeLeft, bool sizeRight, byte typeLeft, byte typeRight, bool leftRelative, bool rightRelative, bool leftRelativeSize, bool rightRelativeSize)
        {
            uint arg1Address = ip + 2,
                arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint arg2Address = arg1Address + arg1Size,
                arg2Size = argSize(sizeRight, typeRight, rightRelative, rightRelativeSize);

            uint left = srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize),
                right = srcArgValue(ip, arg2Address, sizeRight, typeRight, rightRelative, rightRelativeSize);

            setFlag(FLAG_EQ, left == right);
            setFlag(FLAG_LESS, left < right);

            regs.ip = arg2Address + arg2Size;
        }
        void jmp(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
        }
        void je(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (getFlag(FLAG_EQ))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void jne(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (!getFlag(FLAG_EQ))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void jg(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (!getFlag(FLAG_EQ) && !getFlag(FLAG_LESS))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void jl(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (getFlag(FLAG_LESS))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void jle(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (getFlag(FLAG_EQ) || getFlag(FLAG_LESS))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void jge(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            if (!getFlag(FLAG_LESS))
                regs.ip = srcArgValue(ip, ip + 2, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            else
            {
                uint arg1Address = ip + 2,
                    arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
                regs.ip = arg1Address + arg1Size;
            }
        }
        void _int(uint ip, bool sizeLeft, byte typeLeft, bool leftRelative, bool leftRelativeSize)
        {
            uint arg1Address = ip + 2,
                  arg1Size = argSize(sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            uint int_index = srcArgValue(ip, arg1Address, sizeLeft, typeLeft, leftRelative, leftRelativeSize);
            if (int_index != 0) // don't modify IP if we are about to exit
                regs.ip = arg1Address + arg1Size;
            if (int_index == 0)
            {
                done = true;
            }
            else if (int_index == 1)
            {
                //Program.writeOutput((char)regs.d);
            }
            else if (int_index == 2)
            {
                //Program.writeOutput(regs.z.ToString() + Environment.NewLine);
            }
            else if (int_index == 3)
            {
                //regs.d = (byte)Program.readInput();
            }
            else if (int_index == 5)
            {
                uint str_address = regs.v;
                uint str_len = regs.z;
                string str = "";
                for (uint cp = str_address; cp < str_address + str_len; cp++)
                    str += (char)memory[ip, cp];
                //Program.show();
                //System.Windows.Forms.MessageBox.Show(str);
                //Program.writeOutput(str);
            }
            else if (int_index == 6)
            {
                uint str_address = regs.v;
                uint str_len = regs.z;
                string str = "";
                for (uint cp = str_address; cp < str_address + str_len; cp++)
                    str += (char)memory[ip, cp];
                //if (Program.debug_gui)
                //{
                //    Program.show();
                //    System.Windows.Forms.MessageBox.Show(str);
                //}
                //Program.writeDebugOutput(str);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        void iret(uint ip)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
